#include <string.h>
#include "Audio.h"
#include "dumb.h"
#include <SDL/SDL.h>

typedef struct AudioChunk {
	Uint8 *data;
	Uint32 length;
	// volume,pan?
} AudioChunk;

typedef struct AudioChannel {
	AudioChunk *chunk;
	int is_playing;
	int position;
	int volume;
	int pan;
} AudioChannel;

typedef struct Audio {
	int num_channels;
	int sfx_attenuation;
	AudioChannel sfx_channels[16];
	DUH* mus_file;
	DUH_SIGRENDERER *mus_sigrenderer;
} Audio;

AudioChunk sfx_cache[SFX_LAST] = {0};

Audio AUDIO;

void audio_mix(void* userdata, Uint8 *stream, int len);

void audio_init() {
	int i;
	SDL_AudioSpec audio_spec;
	audio_spec.freq = 44100;
	audio_spec.format = AUDIO_S16SYS;
	audio_spec.channels = 2;
	audio_spec.samples = 2048;
	audio_spec.callback = audio_mix;

	SDL_OpenAudio(&audio_spec, NULL);

	AUDIO.num_channels = 16;
	AUDIO.sfx_attenuation = AUDIO.num_channels / 5;
	for(i=0;i<AUDIO.num_channels;i++) {
		AUDIO.sfx_channels[i].is_playing = 0;
		AUDIO.sfx_channels[i].chunk = NULL;
		AUDIO.sfx_channels[i].volume = 0;
		AUDIO.sfx_channels[i].pan = 0;
	}

	atexit(&dumb_exit);

	dumb_register_stdfiles();

	AUDIO.mus_file = NULL;
	AUDIO.mus_sigrenderer = 0;

	SDL_PauseAudio(0);
}



void audio_render_sfx(int channel, Uint8 *stream, int len){
	AudioChannel *c = &AUDIO.sfx_channels[channel];
	if(c->is_playing) {
		if((Uint32)len >= (c->chunk->length - c->position)) {
			SDL_MixAudio(stream, &c->chunk->data[c->position], c->chunk->length - c->position, SDL_MIX_MAXVOLUME);
			c->is_playing = 0;
		}
		else {
			SDL_MixAudio(stream, &c->chunk->data[c->position], len, SDL_MIX_MAXVOLUME);
			c->position += len;
		}
		printf("Channel %d: active=%d, position=%d, data=%p, length=%d\n", channel, c->is_playing, c->position, c->chunk, (c->chunk)?c->chunk->length:-1);
	}

	
}


int audio_next_channel() {
	int i;
	// find first free channel
	for(i=0;i<AUDIO.num_channels;i++)
		if(!AUDIO.sfx_channels[i].is_playing)
			return i;

	return rand()%AUDIO.num_channels; // durrr
}

void audio_render_music(Uint8 *stream, int len) {
	if(!AUDIO.mus_file) return;
	// i hope len is always even!
	duh_render(AUDIO.mus_sigrenderer, 16, 0, 0.33f /* volume! */, 65536.0f/44100.0f, len/4, stream);
}

// the SDL mix callback
// XXX: use high-res mix buffer? doesn't seem necessary
void audio_mix(void* userdata, Uint8 *stream, int len) {
	int i;
	audio_render_music(stream, len);

	for(i=0;i<AUDIO.num_channels;i++)
		audio_render_sfx(i, stream, len);
}


void audio_music(MusicID id, char *filename) {
	SDL_LockAudio();
	if(AUDIO.mus_sigrenderer) duh_end_sigrenderer(AUDIO.mus_sigrenderer);
	if(AUDIO.mus_file) unload_duh(AUDIO.mus_file);
	AUDIO.mus_file = dumb_load_mod_quick(filename);
	AUDIO.mus_sigrenderer = duh_start_sigrenderer(AUDIO.mus_file, 0, 2, 0);
	SDL_UnlockAudio();
}


// load filename as wav and convert to 44khz 16bit stereo and point our next available buffer to it
void audio_sfx(SFXID id, char *filename) {
	int i;
	if(sfx_cache[id].data) {
		SDL_LockAudio();
		i = audio_next_channel();
		AUDIO.sfx_channels[i].chunk = &sfx_cache[id];
		AUDIO.sfx_channels[i].is_playing = 1;
		AUDIO.sfx_channels[i].position = 0;
		SDL_UnlockAudio();
	}
	else {
		SDL_AudioSpec in_spec;
		Uint32 in_length;
		Uint8 *in_buffer;
		SDL_AudioCVT cvt;
		SDL_LoadWAV(filename, &in_spec, &in_buffer, &in_length);

		SDL_BuildAudioCVT(&cvt, 
			in_spec.format, in_spec.channels, in_spec.freq,
			AUDIO_S16SYS,  2, 44100);

		cvt.buf = malloc(in_length * cvt.len_mult);
		cvt.len = in_length;
		memcpy(cvt.buf, in_buffer, in_length);

		SDL_FreeWAV(in_buffer);

		SDL_ConvertAudio(&cvt);

		// decrease volume
		for(i=0;i<cvt.len_cvt;i++) {
			((Sint16*)cvt.buf)[i/2] = 2*((Sint16*)cvt.buf)[i/2]/AUDIO.sfx_attenuation;
		}

		printf("%s converted: rate_incr=%4.4f, len=%d, len_cvt=%d, len_rat=%4.4f\n", filename, cvt.rate_incr, cvt.len, cvt.len_cvt, cvt.len_ratio);
		SDL_LockAudio();

		sfx_cache[id].length = cvt.len_cvt;
		sfx_cache[id].data = cvt.buf;

		i = audio_next_channel();
		AUDIO.sfx_channels[i].chunk = &sfx_cache[id];
		AUDIO.sfx_channels[i].is_playing = 1;
		AUDIO.sfx_channels[i].position = 0;

		SDL_UnlockAudio();
	}
}

void audio_clear_sfx_cache() {
	int i;
	SDL_LockAudio();
	for(i=0;i<AUDIO.num_channels;i++) {
		AUDIO.sfx_channels[i].chunk = NULL;
		AUDIO.sfx_channels[i].is_playing = 0;
		AUDIO.sfx_channels[i].position = 0;	
	}
	for(i=0;i<SFX_LAST;i++) {
		free(sfx_cache[i].data);
		sfx_cache[i].data = NULL;
	}
}

int audio_music_get_row() { return dumb_it_sr_get_current_row(duh_get_it_sigrenderer(AUDIO.mus_sigrenderer)); }
